<template>
  <view class="audio-cc">
    <view class="info-box">
      <view class="title">{{ title }}</view>
      <view v-if="show" class="time">{{ time }}</view>
    </view>
    <img class="play-btn" @click="changeState" mode="widthFix" :src="currentImg">

    <slider class="audio-slider" activeColor="#FFCE00" backgroundColor="#E8E8E8" block-color="#FFCE00" block-size="12"
      v-if="show" :max="sliderMax" :value="sliderCurrent" @change="sliderChange" @changing="sliderChanging" />
  </view>
</template>

<script setup>
  import {
    ref,
    computed,
    watch,
    onMounted,
    onBeforeUnmount
  } from 'vue'

  const AudioPlayerCcContainer = {}

  // Props
  const props = defineProps({
    src: {
      type: String,
      default: ''
    },
    title: {
      type: String,
      default: ''
    },
    autoplay: {
      type: Boolean,
      default: false
    },
    loop: {
      type: Boolean,
      default: false
    },
    firstCurrent: {
      type: Number,
      default: 0
    },
    playbackRate: {
      type: Number,
      default: 1.0
    },
    alonePlay: {
      type: Boolean,
      default: false
    },
    playImg: {
      type: String,
      default: '../../static/audio-play.png'
    },
    pauseImg: {
      type: String,
      default: '../../static/audio-stop.png'
    }
  })

  const emit = defineEmits(['currentChange', 'ended'])

  // Reactive state
  const uuid = ref('')
  const max = ref(0)
  const current = ref(0)
  const playState = ref(false)
  const stopState = ref(false)
  const ready = ref(false)
  const init = ref(false)
  const srcChange = ref(false)
  const accuracy = ref(3000)

  const innerAudioContext = uni.createInnerAudioContext()

  // Computed properties
  const currentImg = computed(() => {
    return playState.value ? props.pauseImg : props.playImg
  })

  const time = computed(() => {
    if (max.value > 0) {
      let end = max.value - current.value
      let minute = parseInt(end / 60)
      let second = parseInt(end % 60)

      if (minute < 10) {
        minute = `0${minute}`
      }

      if (second < 10) {
        second = `0${second}`
      }

      return `${minute}:${second}`
    } else {
      return '00:00'
    }
  })

  const show = computed(() => {
    return max.value > 0
  })

  const sliderMax = computed(() => {
    return max.value > 0 ? parseInt(max.value * accuracy.value) : 0
  })

  const sliderCurrent = computed(() => {
    return current.value > 0 ? parseInt(current.value * accuracy.value) : 0
  })

  // Methods
  const createUuid = () => {
    let s = []
    const hexDigits = '0123456789abcdef'

    for (let i = 0; i < 36; i++) {
      s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1)
    }

    s[14] = '4'
    s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1)
    s[8] = s[13] = s[18] = s[23] = '-'

    uuid.value = s.join('')
  }

  const changeState = () => {
    if (ready.value) {
      playState.value = !playState.value
    } else {
      uni.showToast({
        title: '资源缓冲中',
        duration: 2000,
        icon:'loading'
      })
    }
  }

  const playAudio = () => {
    playState.value = true
  }

  const pauseAudio = () => {
    playState.value = false
  }

  const stopAudio = () => {
    stopState.value = true
    playState.value = false
    stopState.value = false
  }

  const seek = (index) => {
    innerAudioContext.seek(index / accuracy.value)
  }

  const seekAndPlay = (index) => {
    setTimeout(() => {
      seek(index)
      playAudio()
    }, 100)
  }

  const sliderChange = (e) => {
    pauseAudio()
    seekAndPlay(e.detail.value)
  }

  const sliderChanging = (e) => {
    pauseAudio()
  }

  // Audio initialization
  const audioInit = () => {
    if (props.src.length > 0) {
      const time = parseInt(Date.parse(new Date()) / (1000 * 60 * 30))
      const src = `${props.src}?v=${time}`

      console.info(`uuid：${uuid.value}`)
      console.info(`加载音频地址：${src}`)

      innerAudioContext.autoplay = props.autoplay
      innerAudioContext.loop = props.loop
      AudioPlayerCcContainer[uuid.value] = innerAudioContext


    }

    // Event listeners
    innerAudioContext.onCanplay(() => {
      console.info('初始化加载事件')
      ready.value = true
      max.value = innerAudioContext.duration
      if (innerAudioContext.duration <= 0) {
        setTimeout(() => {
          max.value = innerAudioContext.duration
          console.info(`max: ${max.value};src: ${innerAudioContext.src}`)
        }, 200)
      }
    })

    innerAudioContext.onPlay(() => {
      console.info('开始播放')
      if (!playState.value) {
        playAudio()
      }
      if (props.alonePlay) {
        for (const id in AudioPlayerCcContainer) {
          if (uuid.value !== id) {
            AudioPlayerCcContainer[id] && AudioPlayerCcContainer[id].pause()
          }
        }
      }
    })

    innerAudioContext.onTimeUpdate(() => {
      if (srcChange.value) {
        srcChange.value = false
        max.value = innerAudioContext.duration
        console.info(`max: ${max.value};src: ${innerAudioContext.src}`)
      }

      current.value = innerAudioContext.currentTime
    })

    innerAudioContext.onPause(() => {
      console.info('播放暂停')
      if (playState.value) {
        pauseAudio()
      }
    })

    innerAudioContext.onStop(() => {
      console.info('播放停止')
    })

    innerAudioContext.onEnded(() => {
      console.info('播放结束')
      stopAudio()
      emit('ended')
    })

    innerAudioContext.onSeeked(() => {
      console.info('音频完成 seek 操作事件')
    })

    innerAudioContext.onSeeking(() => {
      console.info('音频进行 seek 操作事件')
    })

    innerAudioContext.onError((err) => {
      console.info(err)
      ready.value = false

      const errorMap = {
        10001: "系统错误",
        10002: "网络错误",
        10003: "文件错误",
        10004: "格式错误",
        "-1": "文件加载失败"
      }

      const errorMsg = errorMap[err ?? "-1"]
      uni.showToast({
        title: errorMsg,
        icon: 'error'
      })
    })

    init.value = true
  }

  // Watchers
  watch(playState, (val) => {
    console.info(`play: ${val}`)
    if (props.src.length > 0) {
      if (val) {
        console.info(`uuid：${uuid.value};播放状态切换：播放`)
        innerAudioContext.play()
      } else {
        console.info(`uuid：${uuid.value};播放状态切换：暂停`)
        innerAudioContext.pause()
      }
    }
  })

  watch(stopState, (val) => {
    if (val) {
      console.info(`uuid：${uuid.value};播放状态切换：停止`)
      current.value = 0
      innerAudioContext.stop()
      playState.value = false
      stopState.value = false
    }
  })

  watch(() => props.src, (newVal, oldVal) => {
    if (newVal !== oldVal && init.value) {
      current.value = 0
      innerAudioContext.src = newVal
      srcChange.value = true
    }
  })

  watch(init, (val) => {
    if (val) {
      innerAudioContext.src = props.src
      srcChange.value = true

      if (props.firstCurrent > 0) {
        innerAudioContext.seek(props.firstCurrent)
      }
    }
  })

  watch(() => props.playbackRate, (rate) => {
    innerAudioContext.playbackRate = rate
  })

  watch(() => props.autoplay, (autoplay) => {
    innerAudioContext.autoplay = autoplay
  })

  watch(current, (val) => {
    emit('currentChange', val)
  })

  // Lifecycle hooks
  onMounted(() => {
    createUuid()
    audioInit()
  })

  onBeforeUnmount(() => {
    console.info('实例销毁;播放状态切换：停止')
    innerAudioContext.stop()
    innerAudioContext.destroy()
    AudioPlayerCcContainer[uuid.value] = null
  })
</script>

<style lang="scss">
  .audio-cc {

    margin: auto;
    width: 650rpx;
    height: 130rpx;
    background: #F9F9F9;
    border-radius: 16rpx;
    position: relative;

    .info-box {
      width: 100%;
      padding: 20rpx 30rpx;

      .title {
        height: 40rpx;
        font-size: 28rpx;
        font-family: PingFangSC-Regular, PingFang SC;
        font-weight: 400;
        color: #262626;
        line-height: 40rpx;
      }

      .time {
        height: 34rpx;
        font-size: 24rpx;
        font-family: PingFang-SC-Light, PingFang-SC;
        font-weight: 300;
        color: #262626;
        line-height: 34rpx;
      }
    }

    .play-btn {
      position: absolute;
      width: 80rpx;
      height: 80rpx;
      right: 30rpx;
      top: 20rpx;
    }

    .audio-slider {
      position: absolute;
      width: 555rpx;
      left: 0;
      right: 0;
      bottom: -18rpx;
    }
  }
</style>